import { Rect } from "../../core/Layout.js";
import Component from "../../core/Component.js";
import { ComponentInterface } from "../../core/Component.js";
import CanvasScreen from "./CanvasScreen.js";
import { Handlers } from "./utils/Handlers.js";
import { ClickEvent, DragEvent } from "./utils/EventEmitter.js";

export type OnClickFunc = (clickEvt: ClickEvent) => void

export type CanvasComponentOptions = {
    child?: ComponentInterface
    x?: number
    y?: number
    width?: number
    height?: number
    draggable?: boolean
    clickable?: boolean
    onClick?: OnClickFunc
}

export default abstract class CanvasComponent extends Component {
  context: CanvasRenderingContext2D
  declare screen?: CanvasScreen
  handlers: Handlers
  declare options: CanvasComponentOptions

  constructor(options: CanvasComponentOptions) {
    super(options)
    this.handlers = new Handlers(options)
  }

  async init(): Promise<void> {
    this.context = this.screen.context
    this.handlers.component = this
    this.handlers.screen = this.screen
    await super.init()
  }

  async update(dt: number) {
    await super.update(dt)
    await this.draw(this.context, dt)
  }

  async resize(): Promise<void> {
      super.resize()
      this.handlers.rect = new Rect(this.screenX, this.screenY, this.width, this.height)
  }

  async destroy(): Promise<void> {
      super.destroy()
      if(this.handlers) {
        this.handlers.destroy()
        this.handlers = null
      }
  }

  abstract draw(context: CanvasRenderingContext2D, dt: number): Promise<void>
  async onDrag(dragEvt: DragEvent): Promise<void> {}
  async onClick(clickEvt: DragEvent): Promise<void> {
    if(this.options.onClick) {
      this.options.onClick.call(this, clickEvt)
    }
  }

  async load(): Promise<void> {
    super.load()
    if(this.options.draggable) {
      this.handlers.addEventListener('dragStart', this.onDrag.bind(this))
      this.handlers.addEventListener('dragEnd', this.onDrag.bind(this))
      this.handlers.addEventListener('drag', this.onDrag.bind(this))
    }
    if(this.options.clickable) {
      this.handlers.addEventListener('click', this.onClick.bind(this))
    }
  }
  
  async unload(): Promise<void> {
    if(this.options.draggable) {
      this.handlers.removeEventListener('dragStart', this.onDrag.bind(this))
      this.handlers.removeEventListener('dragEnd', this.onDrag.bind(this))
      this.handlers.removeEventListener('drag', this.onDrag.bind(this))
    }
    if(this.options.clickable) {
      this.handlers.removeEventListener('click', this.onClick.bind(this))
    }
    super.unload()
  }

  getParentBoundary() {
    if(this.parent) {
      var parent = this.parent
      return new Rect(parent.screenX, parent.screenY, parent.width, parent.height)
    }

    if(this.screen) {
      var screen = this.screen
      return new Rect(0, 0, screen.application.screenWidth, screen.application.screenHeight)
    }

    return new Rect(0, 0, window.innerWidth, window.innerHeight)
  }

  inheritBoundary() {
    var rect = this.getParentBoundary()
    if(typeof this.options.width !== 'number') this.width = rect.width
    if(typeof this.options.height !== 'number') this.height = rect.height
    if(typeof this.options.x !== 'number') this.screenX = rect.left
    if(typeof this.options.y !== 'number') this.screenY = rect.top
  }
}